
	/***********************************************************************************************

		@API
		Class: 		jsl.Connector

		Static:
			cn			fromId				(id)
			dt			support				()

		Constructor:
			cn			jsl.Connector	(url, id*)

		Methods:
			id			id						()
			Url			url						(complete*)
			cn			open					(options*)
			cn			close					()
			st			status				()
			cn			send					(data*)

		Events:
			beforeOpen,			open
			beforeClose,		close
			beforeReceive,	receive
			beforeSend,			send
			beforeError,		error

		Global events:
			(all base events)

		Legend:
			status:		idle, opening, open, closing
			data:			Blob, XNode, DataSet, primitive, object

		Global options:
			content: 		auto, text, js, xml, json, blob, obj, csv
			params:			o-

		Ws options:
			protocol

		File options:
			async:			b
			cache:			b
			timeout:		i+
			encoding:		ex: none, utf8:lzss

		Http options:
			async:			b
			cache(get):	b
			jsonp:			b
			timeout:		i+
			method:			get, post, put, delete
			headers:		o-

		Options used on the open phase:
			base:		params
			http:		async, method, headers, jsonp
			file:		async
			ws:			protocol

	************************************************************************************************/

	//-------------------------------------------------------------------------------
	jsl.Class("jsl.Connector", {

		//-------------------------------------------------------------------------------
		inherits: [ jsl.EventMap, jsl.OptionMap ],

		//static:
		//-------------------------------------------------------------------------------
		$__cns: {},

		//-------------------------------------------------------------------------------
		__id:		null,
		__url:	null,
		__prt:	null,		//protocol
		__oq:		null,		//current pending operations queue
		__st: 	"idle",	//current status
		__chs: {},			//cache

		//init:
		//-------------------------------------------------------------------------------
		initializer: function() {
			var id, prt

			prt = jsl.EventMap.prototype
			prt.EventMap.call(this)

			for(id in prt)
				if(typeof prt[id] === "function")
					this[id] = prt[id]

			this.events("beforeOpen", "beforeClose", "beforeSend", "beforeReceive", "beforeError")
			this.events("open", "close", "send", "receive", "error")
		},
		//-------------------------------------------------------------------------------
		$fromId: function(id) {//!
			if(arguments.length !== 1)	throw jsl.ParamError()
			jsl.validate(id, "s+", "id")//!
			return this.__cns[id] || null
		},
		//-------------------------------------------------------------------------------
		$support: function() {//!
			if(arguments.length)	throw jsl.ParamError()//!
			return jsl.__ni_cnt_support()
		},
		//-------------------------------------------------------------------------------
		Connector: function(url, id) {
			var mth, s, lct, prt//!
			if(!arguments.length || arguments.length > 2)	throw jsl.ParamError()
			jsl.validate(url, "url", "url")
			if(arguments.length === 2)	jsl.validate(id, "s+", "id")
			if(arguments.length === 2 && jsl.Connector.__cns[id])	throw jsl.ParamError("AlreadyUsed:id")//!


			lct = jsl.Url(url)
			prt = lct.protocol()//!
			if(!jsl.Connector.support()[prt])	throw jsl.ParamError("NotSupported:" + prt)//!

			this.EventMap()
			this.OptionMap()

			this.__oq		= []
			this.__prt	= prt
			this.__url 	= lct
			if(id) this.__id = id + ""

			this.__defineOptions()

			s = (url + "").match(/^\s*([^:\s\/]+)/)
			s = jsl.Url.scheme(s[1])
			s = s || url + ""

			mth = s.match(/^\s*(?:https?)\((get|post|put|delete)\):\/\//i)
			mth = mth && mth[1]

			if(mth) this.method(mth)

			jsl.__ni_cnt_create(this)
			jsl.Connector.__cns[id] = this
			return this
		},
		//-------------------------------------------------------------------------------
		id: function() {//!
			if(arguments.length)	throw jsl.ParamError()//!
			return this.__id
		},
		//-------------------------------------------------------------------------------
		url: function(complete) {
			var url, pms, id, ls//!
			if(arguments.length > 1)	throw jsl.ParamError()
			if(arguments.length)	jsl.validate(complete, "b", "complete")//!

			if(!arguments.length || !+complete)
				url = this.__url
			else {

				pms = this.params()

				if(pms) {
					ls = []
					for(id in pms) ls.push(id + "=" + pms[id])
					pms = ls.join("&")

					url = this.url()
					url = jsl.Url((url + "").replace(/#[\s\S]+$/, "") + (url.query() ? "&" : "?") + pms +
												(url.fragment() ? "#" + url.fragment() : ""))
				}
				else
					url = this.url()
			}

			return url
		},
		//-------------------------------------------------------------------------------
		open: function(options) {
			var e, obj//!
			if(arguments.length > 1)	throw jsl.ParamError()
			if(arguments.length)			jsl.validate(options, "o", "options")//!

			if(options)
				this.set(options)

			if(this.__oq.length)
				this.__oq.push({ id: "open" })

			else if(this.__st === "idle") {

				e = new jsl.Event
				e.connector = this

				jsl.Connector.throwEvent("beforeOpen", e)
				this.throwEvent("beforeOpen", e)

				obj = this
				this.__st = "opening"
				this.__oq.push({ id: "fn", f: function() {
					obj.__st = "open"
					jsl.Connector.throwEvent("open", e)
					obj.throwEvent("open", e)
				}})

				jsl.__ni_cnt_open(this)
			}

			return this
		},
		//-------------------------------------------------------------------------------
		close: function() {
			var e, obj//!
			if(arguments.length)	throw jsl.ParamError()//!

			if(this.__oq.length)
				this.__oq.push({ id: "close" })

			else if(this.__st === "open") {

				e = new jsl.Event
				e.connector = this

				jsl.Connector.throwEvent("beforeClose", e)
				this.throwEvent("beforeClose", e)

				obj = this
				this.__st = "closing"
				this.__oq.push({ id: "fn", f: function() {
					obj.__st = "idle"
					jsl.Connector.throwEvent("close", e)
					obj.throwEvent("close", e)
				}})

				jsl.__ni_cnt_close(this)
			}

			return this
		},
		//-------------------------------------------------------------------------------
		status: function() {//!
			if(arguments.length)	throw jsl.ParamError()//!
			return this.__st
		},
		//-------------------------------------------------------------------------------
		send: function(data) {
			var s, v, Dt, Ds, e, id, b//!
			if(arguments.length > 1)	throw jsl.ParamError()
			if(!arguments.length && this.url().protocol() === "ws")	throw jsl.ParamError("Expected:data")//!

			v = this
			if(this.__st === "idle")
				this.open()

			//1-opening
			//-------------------------------------------------
			if(this.__st === "opening") this.__oq.push({
				id: "send",
				dt: data
			})

			//2-closing
			//-------------------------------------------------
			else if(this.__st === "closing")
				this.__oq.push({ id: "open" }, { id: "send", dt: data })

			//3-open
			//-------------------------------------------------
			else if(this.__st === "open") {

				if(data != null) {

					Dt = jsl.Data
					Ds = jsl.DataSource

					if(typeof data !== "function" && typeof data !== "object")
						s = data + ""
					else if(Dt)
						if(Dt.Blob.isClassOf(data)) 							s = data.toString(16)
						else if(Dt.XNode.isClassOf(data))					s = Dt.toXml(data)
						else if(Ds && Ds.DataSet.isClassOf(data))	s = Dt.toCsv(data)
						else																			s = data
					else
						s = data
				}

				e = new jsl.Event
				e.connector = this
				e.data			= data
				e.message		= s	|| ""

				jsl.Connector.throwEvent("beforeSend", e)
				this.throwEvent("beforeSend", e)

				b 	= this.cache && this.cache() && (!this.method || this.method() === "get")
				id 	= b && this.url(true) + ""

				if(b) {
					if(id	in this.__chs)	b = 2
					else 	v = jsl.__ni_cnt_send(this, s || "")
				}
				else
					v = jsl.__ni_cnt_send(this, s || "")

				jsl.Connector.throwEvent("send", e)
				this.throwEvent("send", e)

				if(b === 2) v = this.__receive(this.__chs[id])
			}

			return v
		},
		//private
		//-------------------------------------------------------------------------------
		__defineOptions: function() {
			var pt

			pt = this.__prt

			if(pt === "http") {
				this.option("method", 	this.__od_mth)
				this.option("timeout", 	this.__od_tmo)
				this.option("async", 		this.__od_asy)
				this.option("cache", 		this.__od_che)
				this.option("headers", 	this.__od_hds)
				this.option("jsonp", 		this.__od_jsn)
			}

			else if(pt === "ws")
				this.option("protocol",	this.__od_prt)

			else {
				this.option("timeout", 	this.__od_tmo)
				this.option("async", 		this.__od_asy)
				this.option("cache", 		this.__od_che)
				this.option("encoding",	this.__od_enc)
			}
		},
		//-------------------------------------------------------------------------------
		__exec: function() {
			var q, dt

			q = this.__oq
			this.__oq = []

			while(q.length) {

				dt = q.shift()

				if(dt.id === "open" && this.status() === "idle") {
					this.__oq = []
					this.open()
					q = this.__oq.concat(q)
					if(this.status() === "opening") break
				}
				else if(dt.id === "close" && this.status() === "open") {
					this.__oq = []
					this.close()
					q = this.__oq.concat(q)
					if(this.status() === "closing") break
				}
				else if(dt.id === "send") {
					this.__oq = []
					this.send(dt.dt || "")
					q = this.__oq.concat(q)
					if(this.status() === "opening") break
				}
				else if(dt.id === "fn")
					dt.f()
			}

			this.__oq = q
		},
		//private actions
		//-------------------------------------------------------------------------------
		__receive: function(text) {
			var e, c, dt, jsonp

			e = new jsl.Event
			e.connector = this
			e.url	= this.url()

			jsl.Connector.throwEvent("beforeReceive", e)
			this.throwEvent("beforeReceive", e)

			c = this.content()
			jsonp = this.jsonp && this.jsonp()

			if(jsonp) {//!
				if(	(c === "js" || c === "json" || c === "xml" || c === "blob" || c === "object" || c === "csv") &&
						(typeof text !== "string")) throw jsl.Error("BadData")//!
			}
			else if(c === "auto") {
				c = this.url().resource().toLowerCase().match(/\.([^\s.]+)\s*$/)
				c = c && c[1]

				if(!c || (c !== "js" && c !== "json" && c !== "xml" && c !== "bb" && c !== "obj" && c !== "csv" ))
					c = "text"
			}

			try {
				if(c === "js")
					dt = jsl.execute(text)

				else if(c === "json") {
					jsl.execute("jsl.__tmp.v = " + text)
					dt = jsl.__tmp.v
				}
				else if(c === "xml")
					dt = jsl.require("data").fromXml(text)

				else if(c === "bb")
					dt = jsl.require("data").Blob(jsl.Data.Blob.HEX + text)

				else if(c === "obj")
					dt = jsl.require("data").deserialize(text)

				else if(c === "csv")
					dt = jsl.require("data").fromCsv(text)

				else
					dt = text
			}
			catch(e) {
				c === "js"
					?	this.__error(jsl.Error("BadScript", e).	trace(this.__name + ".receive", [ "data", "type=" + c, "url=" + this.url() ]))
					:	this.__error(jsl.Error("BadData", e).		trace(this.__name + ".receive", [ "data", "type=" + c, "url=" + this.url() ]))
			}

			e.message = text
			e.data 		= dt
			jsl.Connector.throwEvent("receive", e)
			this.throwEvent("receive", e)

			if(this.cache && this.cache() && (!this.method || this.method() === "get"))
				this.__chs[this.url(true)] = text

			return dt
		},
		//-------------------------------------------------------------------------------
		__error: function(er, code) {
			var e

			e = new jsl.Event
			e.connector = this
			e.url				= this.url()
			e.error			= er
			e.code 			= code

			jsl.Connector.throwEvent("beforeError", e)
			this.throwEvent("beforeError", e)

			this.__oq = []

			jsl.Connector.throwEvent("error", e)
			this.throwEvent("error", e)

			throw er
		},
		//-------------------------------------------------------------------------------
		__timeout: function(url) {
			this.__error(jsl.TimeoutError("TimeoutError", [ url ] ))
		}
	})

	//events
	//-------------------------------------------------------------------------------
	jsl.Connector.prototype.EventMap()
	jsl.Connector.prototype.events("beforeOpen", "beforeClose", "beforeSend", "beforeReceive", "beforeError")
	jsl.Connector.prototype.events("open", "close", "send", "receive", "error")

	//options
	//-------------------------------------------------------------------------------
	jsl.Connector.prototype.OptionMap()
	//-------------------------------------------------------------------------------
	jsl.Connector.prototype.option("content", {
		exposed: 		true,
		dvalue:			"auto",
		validator:	"si(auto,text,js,xml,json,blob,obj,csv)",
		setter:			function(v) { return v.toLowerCase() }
	})
	//-------------------------------------------------------------------------------
	jsl.Connector.prototype.option("params", {
		exposed: 		true,
		dvalue:			null,
		validator:	"o-",
		getter:			function(v) { return v ? jsl.Object.clone(v) : null },
		setter:			function(v) { return v ? jsl.Object.clone(v) : null }
	})
	//ws options
	//-------------------------------------------------------------------------------
	jsl.Connector.prototype.__od_prt = {
		exposed: 		true,
		dvalue:			null,
		validator:	"s+"
	}
	//http options
	//-------------------------------------------------------------------------------
	jsl.Connector.prototype.__od_mth = {
		exposed: 		true,
		dvalue:			"get",
		validator:	"si(get,post,put,delete)",
		setter:			function(v) { return v.toLowerCase() }
	}
	//-------------------------------------------------------------------------------
	jsl.Connector.prototype.__od_tmo = {
		exposed: 		true,
		dvalue:			0,
		validator:	"i+"
	}
	//-------------------------------------------------------------------------------
	jsl.Connector.prototype.__od_asy = {
		exposed: 		true,
		dvalue:			true,
		validator:	"b"
	}
	//-------------------------------------------------------------------------------
	jsl.Connector.prototype.__od_che = {
		exposed: 		true,
		dvalue:			false,
		validator:	"b"
	}
	//-------------------------------------------------------------------------------
	jsl.Connector.prototype.__od_jsn = {
		exposed: 		true,
		dvalue:			false,
		validator:	"b"
	}
	//-------------------------------------------------------------------------------
	jsl.Connector.prototype.__od_hds = {
		exposed: 		true,
		dvalue:			null,
		validator:	"o-",
		getter:			function(v) { return v ? jsl.Object.clone(v) : null },
		setter:			function(v) { return v ? jsl.Object.clone(v) : null }
	}
	//file options
	//-------------------------------------------------------------------------------
	jsl.Connector.prototype.__od_enc = {
		exposed: 		true,
		dvalue:			"none",
		validator:	"s+",
		setter:			function(v) { return v.toLowerCase() }
	}

